package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.query.SkuQuery;
import com.spzx.product.service.IProductSkuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.product.vo.ProductSkuVo;
import com.spzx.product.vo.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2025-04-29
 */
@Service
@Slf4j
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements IProductSkuService {

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public List<ProductSkuVo> selectTopSale() {
        return baseMapper.selectTopSale();
    }

    /**
     * 分页条件查询商品列表
     *
     * @param skuQuery 查询条件：关键字、分类ID、品牌ID、排序方式
     * @return
     */
    @Override
    public List<ProductSkuVo> selectSkuList(SkuQuery skuQuery) {
        return baseMapper.selectSkuList(skuQuery);
    }

    /**
     * 根据skuId查询商品最新价格
     *
     * @param skuId
     * @return
     */
    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {
        //1.根据商品ID查询实时价格
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductSku::getId, skuId);
        queryWrapper.select(ProductSku::getSalePrice, ProductSku::getMarketPrice);
        //2.封装价格VO
        ProductSku productSku = baseMapper.selectOne(queryWrapper);
        if (productSku != null) {
            SkuPriceVo skuPriceVo = new SkuPriceVo();
            skuPriceVo.setSkuId(skuId);
            skuPriceVo.setSalePrice(productSku.getSalePrice());
            skuPriceVo.setMarketPrice(productSku.getMarketPrice());
            return skuPriceVo;
        }
        return null;
    }

    /**
     * 根据商品ID查询商品sku
     *
     * @param skuId
     * @return
     */
    @Override
    public ProductSku getSkuInfo(Long skuId) {
        //1.优先从Redis获取业务数据，命中返回则直接返回
        //1.1 构建业务数据缓存Key 形式：sku:info:skuId
        String dataKey = "sku:info:" + skuId;
        //1.2 查询Redis缓存数据
        ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
        //1.3 查询有结果则返回
        if (productSku != null) {
            return productSku;
        }

        //2.尝试获取分布式锁，避免大量请求同时访问DB，造成DB压力过载宕机
        //2.1 构建锁Key 锁粒度降低
        String lockName = dataKey + ":lock";
        //2.2 构建当前线程唯一标识
        String lockValue = UUID.randomUUID().toString();
        //2.3 尝试获取锁
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockName, lockValue, 5, TimeUnit.SECONDS);

        //3.获取锁成功执行查询数据库业务
        if (flag) {
            try {
                //3.1 执行查询数据库操作
                productSku = getById(skuId);
                //3.2 缓存数据到Redis中
                //3.2 设置过期时间 基础时间+随机时间 避免群体key失效，缓存雪崩现象之一
                int ttl = 3600 + new Random().nextInt(600);
                redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);
                return productSku;
            } finally {
                //4.业务执行完毕，采用lua脚本释放锁
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end\n";
                RedisScript<Boolean> booleanRedisScript = new DefaultRedisScript<>(script, Boolean.class);
                redisTemplate.execute(booleanRedisScript, Arrays.asList(lockName), lockValue);
            }
        } else {
            //5.获取锁失败，则自旋重试
            log.error("获取锁失败:{},再次尝试自旋", Thread.currentThread().getName());
            return this.getSkuInfo(skuId);
        }
    }

    /**
     * 封装商品规格参数跟SKUID映射关系Map
     *
     * @param productId
     * @return
     */
    @Override
    public Map<String, Long> getSkuSpecValue(Long productId) {
        //1.创建保存映射关系Map
        Map<String, Long> specMap = new HashMap<>();

        //2.根据商品ID查询商品sku列表
        List<ProductSku> productSkuList = list(
                new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, productId)
                        .select(ProductSku::getSkuSpec, ProductSku::getId)
        );
        //3.遍历SKU列表，将sku规格参数跟SKUID封装到Map中
        if (!CollectionUtils.isEmpty(productSkuList)) {
            for (ProductSku sku : productSkuList) {
                specMap.put(sku.getSkuSpec(), sku.getId());
            }
        }
        return specMap;
    }
}
